<?php
/**
*
* Copyright (c) 2009, Dan Myers.
* Parts copyright (c) 2008, Donovan Schönknecht.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
*   this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
*   notice, this list of conditions and the following disclaimer in the
*   documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This is a modified BSD license (the third clause has been removed).
* The BSD license may be found here:
* http://www.opensource.org/licenses/bsd-license.php
*
* Amazon SimpleDB is a trademark of Amazon.com, Inc. or its affiliates.
*
* SimpleDB is based on Donovan Schönknecht's Amazon S3 PHP class, found here:
* http://undesigned.org.za/2007/10/22/amazon-s3-php-class
*/

/**
* Amazon SimpleDB PHP class
*
* @link http://sourceforge.net/projects/php-sdb/
* @version 0.5.0
*/
class SimpleDB
{
	private static $__accessKey; // AWS Access key
	private static $__secretKey; // AWS Secret key

	public static $useSSL = true;
	public static $verifyHost = 1;
	public static $verifyPeer = 1;

	/**
	* Constructor - if you're not using the class statically
	*
	* @param string $accessKey Access key
	* @param string $secretKey Secret key
	* @param boolean $useSSL Enable SSL
	* @return void
	*/
	public function __construct($accessKey = null, $secretKey = null, $useSSL = true) {
		if ($accessKey !== null && $secretKey !== null)
			self::setAuth($accessKey, $secretKey);
		self::$useSSL = $useSSL;
	}

	/**
	* Set AWS access key and secret key
	*
	* @param string $accessKey Access key
	* @param string $secretKey Secret key
	* @return void
	*/
	public static function setAuth($accessKey, $secretKey) {
		self::$__accessKey = $accessKey;
		self::$__secretKey = $secretKey;
	}

	/**
	* Enable or disable VERIFYHOST for SSL connections
	* Only has an effect if $useSSL is true
	*
	* @param boolean $enable Enable VERIFYHOST
	* @return void
	*/
	public static function enableVerifyHost($enable = true) {
		self::$verifyHost = ($enable ? 1 : 0);
	}

	/**
	* Enable or disable VERIFYPEER for SSL connections
	* Only has an effect if $useSSL is true
	*
	* @param boolean $enable Enable VERIFYPEER
	* @return void
	*/
	public static function enableVerifyPeer($enable = true) {
		self::$verifyPeer = ($enable ? 1 : 0);
	}

	/**
	* Create a domain
	*
	* @param string $domain The domain to create
	* @return boolean
	*/
	public static function createDomain($domain) {
		$rest = new SimpleDBRequest($domain, 'CreateDomain', 'POST', self::$__accessKey);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('createDomain', $rest->error);
			return false;
		}

		return true;
	}

	/**
	* Delete a domain
	*
	* @param string $domain The domain to delete
	* @return boolean
	*/
	public static function deleteDomain($domain) {
		$rest = new SimpleDBRequest($domain, 'DeleteDomain', 'DELETE', self::$__accessKey);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('deleteDomain', $rest->error);
			return false;
		}

		return true;
	}

	/**
	* Get a list of domains
	*
	* @return array | false
	*/
	public static function listDomains() {
		$rest = new SimpleDBRequest('', 'ListDomains', 'GET', self::$__accessKey);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('listDomains', $rest->error);
			return false;
		}

		$results = array();
		if (!isset($rest->body->ListDomainsResult))
		{
			return $results;
		}

		foreach($rest->body->ListDomainsResult->DomainName as $d)
		{
			$results[] = (string)$d;
		}

		return $results;
	}

	/**
	* Get a domain's metadata
	*
	* @param string $domain The domain
	* @return array | false
	*/
	public static function domainMetadata($domain) {
		$rest = new SimpleDBRequest($domain, 'DomainMetadata', 'GET', self::$__accessKey);
		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('domainMetadata', $rest->error);
			return false;
		}

		$results = array();
		if (!isset($rest->body->DomainMetadataResult))
		{
			return $results;
		}

		if(isset($rest->body->DomainMetadataResult->ItemCount))
		{
			$results['ItemCount'] = (string)($rest->body->DomainMetadataResult->ItemCount);
		}

		if(isset($rest->body->DomainMetadataResult->ItemNamesSizeBytes))
		{
			$results['ItemNamesSizeBytes'] = (string)($rest->body->DomainMetadataResult->ItemNamesSizeBytes);
		}

		if(isset($rest->body->DomainMetadataResult->AttributeNameCount))
		{
			$results['AttributeNameCount'] = (string)($rest->body->DomainMetadataResult->AttributeNameCount);
		}

		if(isset($rest->body->DomainMetadataResult->AttributeNamesSizeBytes))
		{
			$results['AttributeNamesSizeBytes'] = (string)($rest->body->DomainMetadataResult->AttributeNamesSizeBytes);
		}

		if(isset($rest->body->DomainMetadataResult->AttributeValueCount))
		{
			$results['AttributeValueCount'] = (string)($rest->body->DomainMetadataResult->AttributeValueCount);
		}

		if(isset($rest->body->DomainMetadataResult->AttributeValuesSizeBytes))
		{
			$results['AttributeValuesSizeBytes'] = (string)($rest->body->DomainMetadataResult->AttributeValuesSizeBytes);
		}

		if(isset($rest->body->DomainMetadataResult->Timestamp))
		{
			$results['Timestamp'] = (string)($rest->body->DomainMetadataResult->Timestamp);
		}

		return $results;
	}

	/**
	* Evaluate a select expression on a domain
	*
	* Function provided by Matthew Lanham
	*
	* @param string  $domain The domain being queried
	* @param string  $select The select expression to evaluate.
	* @param string  $nexttoken The token to start from when retrieving results
	* @return array | false
	*/
	public static function select($domain, $select, $nexttoken = null) {
		$rest = new SimpleDBRequest($domain, 'Select', 'GET', self::$__accessKey);

		//print "<H1>$select</H1>";

		if($select != '')
		{
			$rest->setParameter('SelectExpression', $select);
		}
		if($nexttoken !== null)
		{
			$rest->setParameter('NextToken', $nexttoken);
		}

		$rest = $rest->getResponse();

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('query', $rest->error);
			return false;
		}

		$results = array();

		if (!isset($rest->body->SelectResult))
		{
			return $results;
		}

		foreach($rest->body->SelectResult->Item as $i)
		{
			$item = array('Name' => (string)($i->Name), 'Attributes' => array());
			foreach($i->Attribute as $a)
			{
				if(isset($item['Attributes'][(string)($a->Name)]))
				{
					$temp = (array)($item['Attributes'][(string)($a->Name)]);
					$temp[] = (string)($a->Value);
					$item['Attributes'][(string)($a->Name)] = $temp;
				}
				else
				{
					$item['Attributes'][(string)($a->Name)] = (string)($a->Value);
				}
			}
			$results[] = $item;
		}

		return $results;
	}

	/**
	* Run a query on a domain
	*
	* @param string  $domain The domain being queried
	* @param string  $query The query to run.  If blank, retrieve all items.
	* @param integer $maxitems The number of items to return
	* @param string  $nexttoken The token to start from when retrieving results
	* @return array | false
	*/
	public static function query($domain, $query = '', $maxitems = -1, $nexttoken = null) {
		$rest = new SimpleDBRequest($domain, 'Query', 'GET', self::$__accessKey);

		if($query != '')
		{
			$rest->setParameter('QueryExpression', $query);
		}
		if($maxitems > 0)
		{
			$rest->setParameter('MaxNumberOfItems', $maxitems);
		}
		if($nexttoken !== null)
		{
			$rest->setParameter('NextToken', $nexttoken);
		}

		$rest = $rest->getResponse();

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('query', $rest->error);
			return false;
		}

		$results = array();
		if (!isset($rest->body->QueryResult))
		{
			return $results;
		}

		foreach($rest->body->QueryResult->ItemName as $i)
		{
			$results[] = (string)$i;
		}

		return $results;
	}

	/**
	* Run a query on a domain, and get associated attributes as well
	*
	* @param string  $domain The domain being queried
	* @param string  $query The query to run.  If blank, retrieve all items.
	* @param array   $attributes An array of the attributes to retrieve.  If empty, retrieve all attributes.
	* @param integer $maxitems The number of items to return
	* @param string  $nexttoken The token to start from when retrieving results
	* @return array of (itemname, array(attributename, array(values))) | false
	*/
	public static function queryWithAttributes($domain, $query = '', $attributes = array(), $maxitems = -1, $nexttoken = null) {
		$rest = new SimpleDBRequest($domain, 'QueryWithAttributes', 'GET', self::$__accessKey);

		$i = 0;
		foreach($attributes as $a)
		{
			$rest->setParameter('AttributeName.'.$i, $a);
			$i++;
		}

		if($query != '')
		{
			$rest->setParameter('QueryExpression', $query);
		}
		if($maxitems > 0)
		{
			$rest->setParameter('MaxNumberOfItems', $maxitems);
		}
		if($nexttoken !== null)
		{
			$rest->setParameter('NextToken', $nexttoken);
		}

		$rest = $rest->getResponse();

		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('queryWithAttributes', $rest->error);
			return false;
		}

		$results = array();
		if (!isset($rest->body->QueryWithAttributesResult))
		{
			return $results;
		}

		foreach($rest->body->QueryWithAttributesResult->Item as $i)
		{
			$item = array('Name' => (string)($i->Name), 'Attributes' => array());
			foreach($i->Attribute as $a)
			{
				if(isset($item['Attributes'][(string)($a->Name)]))
				{
					$temp = (array)($item['Attributes'][(string)($a->Name)]);
					$temp[] = (string)($a->Value);
					$item['Attributes'][(string)($a->Name)] = $temp;
				}
				else
				{
					$item['Attributes'][(string)($a->Name)] = (string)($a->Value);
				}
			}
			$results[] = $item;
		}

		return $results;
	}

	/**
	* Get attributes associated with an item
	*
	* @param string  $domain The domain containing the desired item
	* @param string  $item The desired item
	* @param integer $attribute A specific attribute to retrieve, or all if unspecified.
	* @return boolean
	*/
	public static function getAttributes($domain, $item, $attribute = null) {
		$rest = new SimpleDBRequest($domain, 'GetAttributes', 'GET', self::$__accessKey);

		$rest->setParameter('ItemName', $item);

		if($attribute !== null)
		{
			$rest->setParameter('AttributeName', $attribute);
		}

		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('getAttributes', $rest->error);
			return false;
		}

		$results = array();
		if (!isset($rest->body->GetAttributesResult))
		{
			return $results;
		}

		foreach($rest->body->GetAttributesResult->Attribute as $a)
		{
			if(isset($results[(string)($a->Name)]))
			{
				$temp = (array)($results[(string)($a->Name)]);
				$temp[] = (string)($a->Value);
				$results[(string)($a->Name)] = $temp;
			}
			else
			{
				$results[(string)($a->Name)] = (string)($a->Value);
			}
		}

		return $results;
	}

	/**
	* Create or update attributes on an item
	*
	* @param string  $domain The domain containing the desired item
	* @param string  $item The desired item
	* @param integer $attributes An array of (name => (value [, replace])),
	*                             where replace is a boolean of whether to replace the item.
	*                             replace is optional, and defaults to false.
	*                             If value is an array, multiple values are put.
	* @return boolean
	*/
	public static function putAttributes($domain, $item, $attributes) {
		$rest = new SimpleDBRequest($domain, 'PutAttributes', 'POST', self::$__accessKey);

		$rest->setParameter('ItemName', $item);

		$i = 0;
		foreach($attributes as $name => $v)
		{
			if(is_array($v['value']))
			{
				foreach($v['value'] as $val)
				{
					$rest->setParameter('Attribute.'.$i.'.Name', $name);
					$rest->setParameter('Attribute.'.$i.'.Value', $val, false);

					if(isset($v['replace']))
					{
						$rest->setParameter('Attribute.'.$i.'.Replace', $v['replace']);
					}
					$i++;
				}
			}
			else
			{
				$rest->setParameter('Attribute.'.$i.'.Name', $name);
				$rest->setParameter('Attribute.'.$i.'.Value', $v['value']);
				if(isset($v['replace']))
				{
					$rest->setParameter('Attribute.'.$i.'.Replace', $v['replace']);
				}
				$i++;
			}
		}

		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('putAttributes', $rest->error);
			return false;
		}

		return true;
	}

	/**
	* Create or update attributes on multiple item
	*
	* Function provided by Matthew Lanham
	*
	* @param string $domain The domain containing the desired item
	* @param array  $items An array of items of (item_name,
												attributes => array(attribute_name => array(value [, replace])))
	*						If replace is omitted it defaults to false.
	*						Optionally, attributes may just be a single string value,
	*							and replace will default to false.
	* @return boolean
	*/
	public static function batchPutAttributes($domain, $items) {
		$rest = new SimpleDBRequest($domain, 'BatchPutAttributes', 'POST', self::$__accessKey);

		$ii = 0;
		foreach($items as $item)
		{
			$rest->setParameter('Item.'.$ii.'.ItemName', $item['name']);

			$i = 0;
			foreach($item['attributes'] as $name => $v)
			{
				if(is_array($v['value']))
				{
					foreach($v['value'] as $val)
					{
						$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Name', $name);
						$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Value', $val, false);

						if(isset($v['replace']))
						{
							$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Replace', $v['replace']);
						}
						$i++;
					}
				}
				else
				{
					$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Name', $name);
					$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Value', $v['value']);
					if(isset($v['replace']))
					{
						$rest->setParameter('Item.'.$ii.'.Attribute.'.$i.'.Replace', $v['replace']);
					}
					$i++;
				}
			}
			$ii++;
		}

		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('batchPutAttributes', $rest->error);
			return false;
		}


		return true;
	}

	/**
	* Delete attributes associated with an item
	*
	* @param string  $domain The domain containing the desired item
	* @param string  $item The desired item
	* @param integer $attributes An array of (name => value)
	*                             value is either a specific value or null.
	*                             setting the value will erase the attribute with the given
	*                             name and value (for multi-valued attributes).
	*                            If array is unspecified, all attributes are deleted.
	* @return boolean
	*/
	public static function deleteAttributes($domain, $item, $attributes = null) {
		$rest = new SimpleDBRequest($domain, 'DeleteAttributes', 'DELETE', self::$__accessKey);

		$rest->setParameter('ItemName', $item);

		if($attributes !== null)
		{
			$i = 0;
			$attribute = '';
			foreach($attributes as $name => $value)
			{
				$rest->setParameter('Attribute.'.$i.'.Name', $name);
				$i++;
			}
		}

		$rest = $rest->getResponse();
		if ($rest->error === false && $rest->code !== 200)
			$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
		if ($rest->error !== false) {
			SimpleDB::__triggerError('deleteAttributes', $rest->error);
			return false;
		}

		return true;
	}

	/**
	* Trigger an error message
	*
	* @internal Used by member functions to output errors
	* @param array $error Array containing error information
	* @return string
	*/
	public static function __triggerError($functionname, $error)
	{
		if($error['curl'])
		{
			trigger_error(sprintf("SimpleDB::%s(): %s", $functionname, $error['code']), E_USER_WARNING);
		}
		else
		{
			foreach($error['Errors'] as $e)
			{
				$message = sprintf("SimpleDB::%s(): %s: %s\n", $functionname, $e['Code'], $e['Message']);
				trigger_error($message, E_USER_WARNING);
			}
		}
	}

	/**
	* Generate the auth string using Hmac-SHA256
	*
	* @internal Used by SimpleDBRequest::getResponse()
	* @param string $string String to sign
	* @return string
	*/
	public static function __getSignature($string) {
		return base64_encode(hash_hmac('sha256', $string, self::$__secretKey, true));
	}
}

final class SimpleDBRequest
{
	private $sdbhost, $verb, $resource = '', $parameters = array();
	public $response;

	/**
	* Constructor
	*
	* @param string $domain Domain name
	* @param string $action SimpleDB action
	* @param string $verb HTTP verb
	* @param string $accesskey AWS Access Key
	* @param string $host The URL of the SimpleDB host
	* @return mixed
	*/
	function __construct($domain, $action, $verb, $accesskey, $host = 'sdb.amazonaws.com') {
		if($domain != '')
		{
			$this->parameters['DomainName'] = $domain;
		}

		$this->parameters['Action'] = $action;
		$this->parameters['Version'] = '2007-11-07';
		$this->parameters['SignatureVersion'] = '2';
		$this->parameters['SignatureMethod'] = 'HmacSHA256';
		$this->parameters['AWSAccessKeyId'] = $accesskey;

		$this->verb = $verb;
		$this->sdbhost = $host;
		$this->response = new STDClass;
		$this->response->error = false;
	}

	/**
	* Set request parameter
	*
	* @param string  $key Key
	* @param string  $value Value
	* @param boolean $replace Whether to replace the key if it already exists (default true)
	* @return void
	*/
	public function setParameter($key, $value, $replace = true) {
		if(!$replace && isset($this->parameters[$key]))
		{
			$temp = (array)($this->parameters[$key]);
			$temp[] = $value;
			$this->parameters[$key] = $temp;
		}
		else
		{
			$this->parameters[$key] = $value;
		}
	}

	/**
	* Get the response
	*
	* @return object | false
	*/
	public function getResponse() {

		$this->parameters['Timestamp'] = gmdate('c');

		$params = array();
		foreach ($this->parameters as $var => $value)
		{
			if(is_array($value))
			{
				foreach($value as $v)
				{
					$params[] = $var.'='.rawurlencode($v);
				}
			}
			else
			{
				$params[] = $var.'='.rawurlencode($value);
			}
		}

		sort($params, SORT_STRING);

		$query = implode('&', $params);

		$strtosign = $this->verb."\n".$this->sdbhost."\n/\n".$query;
		$query .= '&Signature='.rawurlencode(SimpleDB::__getSignature($strtosign));

		$ssl = (SimpleDB::$useSSL && extension_loaded('openssl'));
		$url = ($ssl ? 'https://' : 'http://').$this->sdbhost.'/?'.$query;

		// Basic setup
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleDB/php');

		if(SimpleDB::$useSSL)
		{
			curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, SimpleDB::$verifyHost);
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, SimpleDB::$verifyPeer);
		}

		curl_setopt($curl, CURLOPT_URL, $url);
		curl_setopt($curl, CURLOPT_HEADER, false);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
		curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);

		// Request types
		switch ($this->verb) {
			case 'GET': break;
			case 'PUT': case 'POST':
				curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
			break;
			case 'HEAD':
				curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
				curl_setopt($curl, CURLOPT_NOBODY, true);
			break;
			case 'DELETE':
				curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
			break;
			default: break;
		}

		// Execute, grab errors
		if (curl_exec($curl))
			$this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
		else
			$this->response->error = array(
				'curl' => true,
				'code' => curl_errno($curl),
				'message' => curl_error($curl),
				'resource' => $this->resource
			);

		@curl_close($curl);

		// Parse body into XML
		if ($this->response->error === false && isset($this->response->body)) {
			$this->response->body = simplexml_load_string($this->response->body);

			// Grab SimpleDB errors
			if (!in_array($this->response->code, array(200, 204))
				&& isset($this->response->body->Errors)) {
				$this->response->error = array('curl' => false, 'Errors' => array());
				foreach($this->response->body->Errors->Error as $e)
				{
					$err = array('Code' => (string)($e->Code),
								 'Message' => (string)($e->Message)
							//	 , 'BoxUsage' => (string)($e->BoxUsage)
								 );
					$this->response->error['Errors'][] = $err;
				}
				unset($this->response->body);
			}
		}

		return $this->response;
	}

	/**
	* CURL write callback
	*
	* @param resource &$curl CURL resource
	* @param string &$data Data
	* @return integer
	*/
	private function __responseWriteCallback(&$curl, &$data) {
		$this->response->body .= $data;
		return strlen($data);
	}
}

